﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Windows;
using Silvermoon.Windows.Styles;
using Silvermoon.Shapes;
using Silvermoon.OpenGL;
using Silvermoon.Controls.Forms;
using System.Drawing;
using Silvermoon.UI;
using Silvermoon.Controls;
using System.Collections.ObjectModel;

namespace Silvermoon.Controls
{
    public class PopupMenu : ContextMenuBase, IItemHost
    {
        #region classes

        class MenuItemCollection : Collection<Item>
        {
            private PopupMenu menu;

            public MenuItemCollection(PopupMenu menu)
                : base()
            {
                this.menu = menu;
            }

            protected override void ClearItems()
            {
                foreach (var item in this)
                {
                    menu.DetachItem(item);
                    item.Dispose();
                }
                base.ClearItems();
                OnCollectionChanged();
            }

            protected override void InsertItem(int index, Item item)
            {
                if (Count >= 5) throw new ArgumentOutOfRangeException("Item collection exceeds limit of 5 items.");
                base.InsertItem(index, item);
                menu.AttachItem(item, index);
                OnCollectionChanged();
            }

            protected override void RemoveItem(int index)
            {
                Item item = this[index];
                menu.DetachItem(item);
                item.Dispose();
                base.RemoveItem(index);
                OnCollectionChanged();
            }

            private void OnCollectionChanged()
            {
                menu.OnCollectionChanged();
            }
        }

        #endregion
        #region fields

        private static readonly StyleDictionary defaultStyles = CreateDefaultStyle();

        private Margin padding = new Margin(4);
        private Alignment verticalAlignment = Alignment.Center;
        private Alignment horizontalAlignment = Alignment.Center;
        private ScaleTransform scale;
        private StretchTransform stretch;
        private float relativeWidth = 1f;
        private float relativeHeight = 1f;
        private MenuItemCollection items;
        private int itemHeight = 64;


        #endregion
        #region accessors

        public static readonly PropertyAccessor<PopupMenu, float> RelativeWidthProperty = PropertyAccessor.Register<PopupMenu, float>(
            "RelativeWidth",
            c => c.RelativeWidth,
            (c, v) => c.RelativeWidth = v);

        public static readonly PropertyAccessor<PopupMenu, float> RelativeHeightProperty = PropertyAccessor.Register<PopupMenu, float>(
            "RelativeHeight",
            c => c.RelativeHeight,
            (c, v) => c.RelativeHeight = v);

        #endregion
        #region ctor

        public PopupMenu()
            : base()
        {
            Initialize();
        }

        protected override void Dispose(bool dispose)
        {
            Items.Clear();
            base.Dispose(dispose);
        }

        #endregion
        #region props

        public Margin Padding
        {
            get { return padding; }
            set
            {
                if (value == null) value = new Margin(0);
                if (!padding.Equals(value))
                {
                    padding = value;
                    Invalidate(InvalidationFlag.Layout);
                }
            }
        }

        public Alignment VerticalAlignment
        {
            get { return verticalAlignment; }
            set { verticalAlignment = value; }
        }

        public Alignment HorizontalAlignment
        {
            get { return horizontalAlignment; }
            set { horizontalAlignment = value; }
        }

        protected IBubble BubbleShape { get { return Background as IBubble; } }


        public int ItemHeight { get { return itemHeight; } }

        public float RelativeWidth
        {
            get
            {
                return relativeWidth;
            }
            set
            {
                if (value <= 0f) value = 0f; else if (value > 1f) value = 1f;
                if (relativeWidth != value)
                {
                    relativeWidth = value;
                    OnRelativeWidthChanged();
                }
            }
        }

        public float RelativeHeight
        {
            get
            {
                return relativeHeight;
            }
            set
            {
                if (value <= 0f) value = 0f; else if (value > 1f) value = 1f;
                if (relativeHeight != value)
                {
                    relativeHeight = value;
                    OnRelativeHeightChanged();
                }
            }
        }

        public IList<Item> Items
        {
            get { return items; }
        }

        public override Size Size
        {
            get
            {
                Size size = base.Size;
                size.Height = GetHeight();
                return size;
            }
            set { base.Size = value; }
        }

        private int GetHeight()
        {
            IBubble bubble = BubbleShape;
            int vSpace = bubble != null ? bubble.GetVerticalSpace() : 0;

            int h = this.padding.Height + ItemHeight * items.Count + vSpace;
            return h;
        }


        #endregion
        #region events

        public event EventHandler<ItemEventArgs> ItemTap;
        #endregion
        #region methods

        protected override void LayoutChildren()
        {
            // already done in LayoutChildren, so do nothing here.
        }

        /// <summary>
        /// Calculates the location of the tooltip based on AnchorControl and AnchorPoint.
        /// </summary>
        public override void CalculatePosition()
        {
            CalculatePositionInternal();
        }

        private void CalculatePositionInternal()
        {
            if (AnchorControl == null) return;

            Rectangle anchorBounds = AnchorBounds;
            Point location = AnchorControl.PointToScreen(anchorBounds.Location);
            location.X += anchorBounds.Width / 2;
            int w2 = Width / 2;
            int offset = w2;
            int top = location.Y - Height;
            Placement placement = Placement.Bottom;
            if (top < 0)
            {
                top = location.Y + anchorBounds.Height;
                placement = Placement.Top;
            }

            int left = location.X - offset;

            if (left < 0)
            {
                offset += left;
                left = 0;
            }
            else if ((left + Width) > Screen.Width)
            {
                int d = left + Width - Screen.Width;
                offset += d;
                left -= d;
            }

            if (top < 0)
            {
               // placement = Placement.Hide;
                top = 0;
            }
            else if ((top + Height) > Screen.Height)
            {
                top = Screen.Height - Height;
               // placement = Placement.Hide;
            }
            this.Location = new Point(left, top);

            IBubble bubble = BubbleShape;
            if (bubble != null)
            {
                bubble.ArrowPlacement = placement;
                bubble.ArrowOffset = offset - w2;
            }
        }

        protected internal override void OnTouchUp(TouchEventArgs e)
        {
            e.Cancel = true;
            e.FocusSet = true;
            base.OnTouchUp(e);
        }

        protected internal override void OnTouchDown(TouchEventArgs e)
        {
            e.Cancel = true;
            e.FocusSet = true;
            base.OnTouchDown(e);
        }

        private void Initialize()
        {
            Styles = defaultStyles;

            items = new MenuItemCollection(this);

            scale = new ScaleTransform();
            stretch = new StretchTransform();
            Transformation += scale;
            Transformation += stretch;
            IsHitTestVisible = false;
            RelativeHeight = 0f;
            RelativeWidth = 0f;
            TextColor = PhoneColors.Black;
            Margin = Margin.Centered;
        }

        protected virtual void OnItemTap(Item item)
        {
            Hide();
            if (ItemTap != null) ItemTap(this, new ItemEventArgs(item.ItemIndex, item, null, this));
        }

        protected override VisualStateManager GetDefaultVisibilityManager()
        {
            //return FormVisibilityManager.DefaultDialog;
            return FormVisibilityManager.DefaultContextMenu;
        }

        protected override void OnSizeChanged()
        {
            base.OnSizeChanged();
            OnRelativeHeightChanged();
            OnRelativeWidthChanged();
        }

        protected virtual void OnRelativeHeightChanged()
        {
            float h = Height;
            float min = h > 0f ? 2f / h : 0f;
            stretch.Y = (1f - min) * relativeHeight + min;
            Invalidate(InvalidationFlag.Transformation);
        }

        protected virtual void OnRelativeWidthChanged()
        {
            float w = Width * 1.5f;
            float min = w > 0f ? 1f / w : 0f;
            scale.X = (1f - min) * relativeWidth + min;
            Invalidate(InvalidationFlag.Transformation);
        }

        protected override void OnShowing()
        {
            Invalidate(InvalidationFlag.Layout);
            base.OnShowing();
            //CalculatePositionInternal();
            Screen.FocusChanged += new EventHandler(OnScreenFocusChanged);
        }

        void OnScreenFocusChanged(object sender, EventArgs e)
        {
            Control focused = Screen.FocusedControl;
            if (focused != this && !this.IsAncestorOf(focused))
            {
                Hide();
            }
        }

        protected override void OnHiding()
        {
            Screen.FocusChanged -= OnScreenFocusChanged;
            base.OnHiding();
        }

        protected override void OnLayout()
        {
            base.OnLayout();
            if (CheckFlag(InvalidationFlag.Size | InvalidationFlag.Layout))
            {
                LayoutItems();
            }
        }

        private void LayoutItems()
        {

            int top = padding.TopZero;
            int left = padding.LeftZero;
            int width = Width - padding.Width;
            int h = itemHeight;

            IBubble bubble = BubbleShape;
            if (bubble != null && bubble.ArrowPlacement == Placement.Top)
            {
                top += bubble.GetVerticalSpace();
            }
            foreach (Item item in items)
            {
                Rectangle itemBounds = new Rectangle(left, top, width, ItemHeight);
                item.Bounds = item.Margin.LayoutChild(itemBounds);
                top += h;
            }
        }

        protected override Shape CreateDefaultBackground()
        {
            //            return new ContextMenuShape();
            return new ShadowedBubbleShape { RenderStyle = RenderStyle.Gradiented, Color = Color.LightGray };
        }

        private static StyleDictionary CreateDefaultStyle()
        {
            var result = new StyleDictionary();
            result.SetClass<Item>(new Style { Background = new RoundedEdgeShape(), Color = PhoneColors.Transparent, StateManager = ItemStateManagers.MenuItemManager });
            return result;
        }

        protected virtual internal void OnCollectionChanged()
        {
            //Invalidate(InvalidationFlag.Layout);
            //OnSizeChanging(Size);
        }

        protected override void OnSizeChanging(Size oldValue)
        {
            base.OnSizeChanging(oldValue);
        }

        internal void AttachItem(Item item, int index)
        {
            VisibleControls.Add(item);
            item.Attach(index, null, this);
        }

        internal void DetachItem(Item item)
        {
            VisibleControls.Remove(item);
            item.Release();
        }

        #endregion
        #region IItemHost Members

        int IItemHost.SelectedItemIndex
        {
            get
            {
                return -1;
            }
            set
            {

            }
        }

        bool IItemHost.IsDynamicItemHeightSupported
        {
            get { return false; }
        }

        public void SetItemHeight(int index, int height)
        {
        }

        void IItemHost.OnTapItem(Item item)
        {
            OnItemTap(item);
        }

        void IItemHost.BringItemIntoView(int itemIndex, bool animated)
        {

        }

        void IItemHost.BringItemRectIntoView(int itemIndex, Rectangle rect, bool animated)
        {

        }

        bool IItemHost.IsItemSelected(Item item)
        {
            return false;
        }

        Item IItemHost.GetRealizedItem(int index)
        {
            return items[index];
        }

        System.Collections.IList IItemHost.ItemsSource
        {
            get { return items; }
        }

        #endregion
    }

}
